/* Nokia 6100 Color LCD routines
   Author: Martin Maly, www.uelectronics.info, author@uelectronics.info

   Initialization and pixel operations are based on these SW:
   free code by Refik Hadzialic (http://www.e-dsp.com) 
   free code by Thomas Pfeifer's (http://thomaspfeifer.net/nokia_6100_display.htm) 
   free code by Owen Osborn (http://www.sparkfun.com/datasheets/LCD/Nokia6100_Demo.c)


  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


  Target: Luminary Micro LM3S811
  Family: ARM7-TDMI Cortex M3
  Compiler: Keil ARM C
*/

#include "hw_ints.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "debug.h"
#include "gpio.h"
#include "sysctl.h"
#include "lcd.h"
#include "lcd12.h"
#include "font.h"

#define LCD_CS GPIO_PIN_0
#define LCD_RST GPIO_PIN_1
#define LCD_CLK GPIO_PIN_2
#define LCD_DATA GPIO_PIN_5

void waitms(int a){
 long int i;
 long int j;
 j=1000*a;
 for (i=0;i<j;i++){ ; }
}

#define CS0 GPIOPinWrite(GPIO_PORTA_BASE, LCD_CS, 0x00);
#define CS1 GPIOPinWrite(GPIO_PORTA_BASE, LCD_CS, 0xFF);
#define RESET0 GPIOPinWrite(GPIO_PORTA_BASE, LCD_RST, 0x00);
#define RESET1 GPIOPinWrite(GPIO_PORTA_BASE, LCD_RST, 0xFF);
#define CLK0 GPIOPinWrite(GPIO_PORTA_BASE, LCD_CLK, 0x00);
#define CLK1 GPIOPinWrite(GPIO_PORTA_BASE, LCD_CLK, 0xFF);
#define SDA0 GPIOPinWrite(GPIO_PORTA_BASE, LCD_DATA, 0x00);
#define SDA1 GPIOPinWrite(GPIO_PORTA_BASE, LCD_DATA, 0xFF);

void shiftBits(byte b) {
  CLK0
  if ((b&128)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&64)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&32)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&16)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&8)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&4)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&2)!=0) SDA1 else SDA0
  CLK1

  CLK0
  if ((b&1)!=0) SDA1 else SDA0
  CLK1
}


//send data
void sendData(byte data) {

  CLK0
  SDA1                                                 //1 for param
  CLK1

  shiftBits(data);
}

//send cmd
void sendCMD(byte data) {

  CLK0
  SDA0                                                 //1 for cmd
  CLK1

  shiftBits(data);
}


void LCD_put_pixel(byte x,byte y,byte color){
  y += 2;          // for some reason starts at 2
  sendCMD(PASET);   // page start/end ram
  sendData(y);            
  sendData(y);          
  sendCMD(CASET);   // column start/end ram
  sendData(x);  
  sendData(x); 
  sendCMD(RAMWR);   // write data
  sendData(color);
}


void LCD_hline (byte x0,byte x1,byte y,byte color){
 int i;
 y+=2;
 sendCMD(PASET);
 sendData(y);
 sendData(y);
 sendCMD(CASET);
 sendData(x0);
 sendData(x1);
 sendCMD(RAMWR);
 for (i=x0;i<=x1;i++) sendData(color);
}
void LCD_vline (byte x,byte y0,byte y1,byte color){
 int i;
 y0+=2;
 y1+=2;
 sendCMD(PASET);
 sendData(y0);
 sendData(y1);
 sendCMD(CASET);
 sendData(x);
 sendData(x);
 sendCMD(RAMWR);
 for (i=y0;i<=y1;i++) sendData(color);
 sendCMD(NOP); //just for fun
}

void LCD_rectangle(byte x0,byte y0,byte x1,byte y1,byte color){
 int i;
 y0+=2;
 y1+=2;
 sendCMD(PASET);
 sendData(y0);
 sendData(y1);
 sendCMD(CASET);
 sendData(x0);
 sendData(x1);
 sendCMD(RAMWR);
 i=(y1-y0+1) * (x1-x0+1);
 while (i) {sendData(color);i--;}
 sendCMD(NOP); //just for fun
}



void LCD_init(void){
	int i;

	//ARM-specific peripherial init
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	GPIODirModeSet(GPIO_PORTA_BASE, LCD_CS, GPIO_DIR_MODE_OUT);
	GPIODirModeSet(GPIO_PORTA_BASE, LCD_RST, GPIO_DIR_MODE_OUT);
	GPIODirModeSet(GPIO_PORTA_BASE, LCD_CLK, GPIO_DIR_MODE_OUT);
	GPIODirModeSet(GPIO_PORTA_BASE, LCD_DATA, GPIO_DIR_MODE_OUT);

	// Display reset etc.

	CS0
  	SDA0
  	CLK1

  	RESET1
  	RESET0
  	RESET1

  	CLK1
  	SDA1
  	CLK1

  	waitms(10);

  	sendCMD(DISCTL);
  	sendData(0x03);
  	sendData(32);
  	sendData(12);
  	sendData(0x00);
  
  	sendCMD(COMSCN);  // common scan direction
  	sendData(0x01);

  	sendCMD(OSCON);  // oscillator on

  	sendCMD(SLPOUT);  // sleep out

  	sendCMD(VOLCTR);  // electronic volume, this is kinda contrast/brightness
  	sendData(5);//ff);       //  this might be different for individual LCDs
  	sendData(0x01);//01);     //

  	sendCMD(PWRCTR);  // power ctrl
  	sendData(0x0f);      //everything on, no external reference resistors
  	waitms(100);

  	sendCMD(DISINV);  // display mode


  	sendCMD(DATCTL);  // datctl
  	sendData(0x00);
  	sendData(0);
  	sendData(0x02); //12bpp by default
  	sendData(0x00);

  	sendCMD(NOP);  // nop

	LCD12_clear(0x0000);

  	sendCMD(DISON);   // display on

  	waitms(200);

 	for (i = 0; i < 150; i++){   // this loop adjusts the contrast, change the number of iterations to get
   		sendCMD(VOLUP);                // desired contrast.  This might be different for individual LCDs
   		waitms(2);
 	}

}

void LCD_clear(byte color){  
	int i;

    sendCMD(PASET);   // page start/end ram
  	sendData(0);            // for some reason starts at 2
  	sendData(131);          
  
  	sendCMD(CASET);   // column start/end ram
  	sendData(0);          
  	sendData(131);


  	sendCMD(RAMWR);    // write some stuff (background)
  	for (i = 0; i < 17424; i++){
		sendData(color);
  	}
}

void LCD_8bpp(void){
  	sendCMD(DATCTL);  // datctl
  	sendData(0x00);
  	sendData(0);
  	sendData(0x01); //0x01 for 8-bit palleted
  	sendData(0x00);
}
void LCD_12bpp(void){
  	sendCMD(DATCTL);  // datctl
  	sendData(0x00);
  	sendData(0);
  	sendData(0x02); //0x02 for 12-bit palleted
  	sendData(0x00);
}

void LCD_palette(void){
	sendCMD(RGBSET8);   // setup color lookup table
    // color table
    //RED
    sendData(0);
    sendData(2);
    sendData(4);
    sendData(6);
    sendData(8);
    sendData(10);
    sendData(12);
    sendData(15);
    // GREEN
    sendData(0);
    sendData(2);
    sendData(4);
    sendData(6);
    sendData(8);
    sendData(10);
    sendData(12);
    sendData(15);
    //BLUE
    sendData(0);
    sendData(4);
    sendData(9);
    sendData(15);
}


//color8 - converts "full-size" 0x0RGB color
//to 8bit paletted value (only for standard 3-3-2 palette)
byte color8(word color){
 byte c;
 byte r,g,b;
 r=(color & 0x0f00)>>8;
 g=(color & 0x00f0)>>4;
 b=(color & 0x000f);
 c=((r&0x0e)<<4) | ((g & 0x0e)<<1) | ((b & 0x0c)>>2);
 return c;
}

//nonoptimized Bresenham line algorithm
void LCD_line ( byte x1, byte y1, byte x2, byte y2, byte color ){
    int dx, dy, stepx, stepy, fraction;

    dy = y2 - y1;
    dx = x2 - x1;

    if ( dy < 0 )
    {
        dy    = -dy;
        stepy = -1;
    }
    else
    {
        stepy = 1;
    }

    if ( dx < 0 )
    {
        dx    = -dx;
        stepx = -1;
    }
    else
    {
        stepx = 1;
    }

    dx <<= 1;
    dy <<= 1;

    LCD_put_pixel( x1, y1, color );

    if ( dx > dy )
    {
        fraction = dy - (dx >> 1);
        while ( x1 != x2 )
        {
            if ( fraction >= 0 )
            {
                y1 += stepy;
                fraction -= dx;
            }
            x1 += stepx;
            fraction += dy;
            LCD_put_pixel( x1, y1, color );
        }
    }
    else
    {
        fraction = dx - (dy >> 1);
        while ( y1 != y2 )
        {
            if ( fraction >= 0 )
            {
                x1 += stepx;
                fraction -= dy;
            }
            y1 += stepy;
            fraction += dx;
            LCD_put_pixel( x1, y1, color );
        }
    }

}
void LCD_circle_fill(int xc, int yc, int r, byte color){
      int x = 0, y = r, d = 2 * (1 - r);

      while (y >= 0)
      {
            LCD_hline(xc - x,xc + x, yc + y,  color);
            LCD_hline(xc - x,xc + x, yc - y,  color);
            if(d + y > 0)
            {
                  y--;
                  d -= (2 * y * LCD_WIDTH / LCD_HEIGTH) - 1;
            }
            if(x > d)
            {
                  x++;
                  d += (2 * x) + 1;
            }
      }
}
void LCD_circle(int xc, int yc, int r, byte color){
      int x = 0, y = r, d = 2 * (1 - r);

      while (y >= 0)
      {
            LCD_put_pixel(xc - x,yc + y,  color);
            LCD_put_pixel(xc - x,yc - y,  color);
            LCD_put_pixel(xc + x,yc + y,  color);
            LCD_put_pixel(xc + x,yc - y,  color);
            if(d + y > 0)
            {
                  y--;
                  d -= (2 * y * LCD_WIDTH / LCD_HEIGTH) - 1;
            }
            if(x > d)
            {
                  x++;
                  d += (2 * x) + 1;
            }
      }
}
